// robot_tcp_server.cpp
#include <QCoreApplication>
#include "robot_tcp_server.h"
#include "../include/seer/SCHeadData.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QTimer>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp/executors/single_threaded_executor.hpp>
#include <chrono>
// RobotTCPServer类构造函数
// 初始化ROS2节点和TCP服务器，设置日志记录器，并连接新连接信号
// @param parent 父对象指针，默认为nullptr
RobotTCPServer::RobotTCPServer(QObject *parent)
    : QObject(parent)
    , rclcpp::Node("robot_tcp_server")  // 初始化ROS2节点
    , m_tcpServer(new QTcpServer(this))
{
    LOG_INFO("初始化开始");
    connect(m_tcpServer, &QTcpServer::newConnection, this, &RobotTCPServer::onNewConnection);
}

// RobotTCPServer类析构函数
// 停止服务器并清理资源
RobotTCPServer::~RobotTCPServer()
{
    stopServer();
}

// 启动服务器
// @param port 服务器监听端口
// @return 启动成功返回true，失败返回false
bool RobotTCPServer::startServer(quint16 port)
{
    if (!m_tcpServer->listen(QHostAddress::Any, port)) {
       LOG_ERROR("无法启动服务器: {}", m_tcpServer->errorString().toStdString());
        return false;
    }
    
    LOG_INFO("服务器启动成功，监听端口: port={}", port);
    return true;
}

// 停止服务器
// 关闭TCP服务器并清理所有客户端连接资源
void RobotTCPServer::stopServer()
{
    m_tcpServer->close();
    
    // 清理所有客户端连接
    for (auto it = m_clientTimers.begin(); it != m_clientTimers.end(); ++it) {
        it.key()->disconnectFromHost();
        it.value()->stop();
        it.value()->deleteLater();
    }
    
    m_clientTimers.clear();
    m_clientBuffers.clear();
}

/*
 * @brief 处理新客户端连接
 * 
 * 当有新的客户端连接到服务器时，此函数会被调用。函数会为每个新连接的客户端
 * 创建必要的资源，包括连接信号槽、创建定时器和初始化缓冲区。
 * 
 * 主要功能：
 * 1. 接受所有待处理的客户端连接
 * 2. 为每个客户端连接设置断开和数据可读信号处理
 * 3. 为每个客户端创建专用的超时定时器
 * 4. 初始化客户端数据缓冲区
 * 5. 记录新连接日志
 */
void RobotTCPServer::onNewConnection()
{
    while (m_tcpServer->hasPendingConnections()) {
        QTcpSocket* clientSocket = m_tcpServer->nextPendingConnection();
        
        // 连接客户端断开和数据可读信号到对应槽函数
        connect(clientSocket, &QTcpSocket::disconnected, this, &RobotTCPServer::onClientDisconnected);
        connect(clientSocket, &QTcpSocket::readyRead, this, &RobotTCPServer::onReadyRead);
        
        // 为每个客户端创建一个定时器
        QTimer* timer = new QTimer(this);
        timer->setSingleShot(true);
        connect(timer, &QTimer::timeout, this, &RobotTCPServer::onSendResponseTimeout);
        
        m_clientTimers[clientSocket] = timer;
        m_clientBuffers[clientSocket] = QByteArray();
        
        LOG_INFO("新客户端连接: {} : {}",clientSocket->peerAddress().toString().toStdString(),clientSocket->peerPort());
    }
}

/**
 * @brief 处理客户端断开连接事件
 * 
 * 当客户端断开连接时，该函数会被调用。它负责清理与该客户端相关的所有资源，
 * 包括定时器、缓冲区以及套接字连接。
 * 
 * @param clientSocket 发送断开连接信号的客户端套接字
 */
void RobotTCPServer::onClientDisconnected()
{
    QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender());
    if (clientSocket) {
        LOG_INFO("客户端断开连接:{} : {}",clientSocket->peerAddress().toString().toStdString(),clientSocket->peerPort());
        
        // 清理客户端相关资源
        if (m_clientTimers.contains(clientSocket)) {
            QTimer* timer = m_clientTimers.take(clientSocket);
            timer->stop();
            timer->deleteLater();
        }
        
        m_clientBuffers.remove(clientSocket);
        clientSocket->deleteLater();
    }
}

/**
 * @brief 处理客户端Socket可读事件
 * 
 * 当客户端Socket有数据可读时，该函数会被调用。函数负责读取客户端发送的数据，
 * 将数据包缓存并解析，然后处理完整的数据包。
 * 
 * 数据包格式要求：
 * - 包头以0x5A开始
 * - 包头包含数据长度信息
 * - 数据以大端字节序存储
 * 
 * 处理流程：
 * 1. 从Socket读取所有可用数据并添加到客户端缓冲区
 * 2. 在缓冲区中查找完整的数据包
 * 3. 验证包头有效性
 * 4. 解析包头获取数据长度
 * 5. 检查数据完整性
 * 6. 提取完整数据包并处理
 * 
 * 对于不完整的数据包，函数会保留它们在缓冲区中，等待更多数据到达。
 */
void RobotTCPServer::onReadyRead()
{
    QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender());
    if (!clientSocket)
        return;
    
    // 读取数据并添加到缓冲区
    QByteArray data = clientSocket->readAll();
    m_clientBuffers[clientSocket].append(data);
    
    // 处理缓冲区中的完整数据包
    QByteArray& buffer = m_clientBuffers[clientSocket];
    while (buffer.size() >= static_cast<int>(sizeof(SeerHeader))) {
        // 检查包头
        if (static_cast<uint8_t>(buffer[0]) != 0x5A) {
            // 包头错误，移除第一个字节
            buffer.remove(0, 1);
            continue;
        }
        
        // 解析包头
        SeerHeader header;
        memcpy(&header, buffer.data(), sizeof(SeerHeader));
        
        // 转换字节序
        uint32_t dataLength = qFromBigEndian(header.m_length);
        
        // 检查数据是否完整
        if (buffer.size() < static_cast<int>(sizeof(SeerHeader) + dataLength)) {
            // 数据不完整，等待更多数据
            break;
        }
        
        // 提取完整数据包
        QByteArray packet = buffer.left(sizeof(SeerHeader) + dataLength);
        buffer.remove(0, sizeof(SeerHeader) + dataLength);
        
        // 处理数据包
        processClientRequest(clientSocket, packet);
    }
}

/**
 * @brief 处理来自客户端的请求数据
 * 
 * 该函数解析客户端发送的数据，提取请求头信息，根据命令类型进行相应处理。
 * 主要处理日志下载相关的命令（7000-7999范围），并对请求数据进行JSON解析。
 * 
 * @param clientSocket 客户端套接字连接指针
 * @param requestData 客户端发送的原始请求数据
 */
void RobotTCPServer::processClientRequest(QTcpSocket* clientSocket, const QByteArray& requestData)
{
    if (requestData.size() < static_cast<int>(sizeof(SeerHeader)))
        return;
    
    // 解析包头
    SeerHeader header;
    memcpy(&header, requestData.data(), sizeof(SeerHeader));
    
    // 转换字节序
    uint16_t cmd = qFromBigEndian(header.m_type);
    uint16_t reqId = qFromBigEndian(header.m_number);
    uint32_t dataLength = qFromBigEndian(header.m_length);
    
    LOG_INFO("收到客户端请求 - 命令:{} 请求ID:{} 数据大小:{}", cmd, reqId, dataLength);
    
    // 根据命令类型处理请求
    if (cmd >= 7000 && cmd < 7999) {
        // 日志下载命令
        // 检查是否有数据部分
        if (dataLength > 0 && requestData.size() >= static_cast<int>(sizeof(SeerHeader) + dataLength)) {
            // 提取数据部分
            QByteArray data = requestData.mid(sizeof(SeerHeader), dataLength);
            QJsonDocument doc = QJsonDocument::fromJson(data);
            if (!doc.isNull() && doc.isObject()) {
                handleCommand(clientSocket, cmd, doc.object(), reqId);
            } else {
                // 发送错误响应
                QJsonObject errorResponse;
                errorResponse["ret_code"] = -1;
                errorResponse["message"] = "Invalid JSON data";
                QJsonDocument responseDoc(errorResponse);
                sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
            }
        } else {
            // 只有header，没有数据部分，直接处理命令
            handleCommand(clientSocket, cmd, QJsonObject(), reqId);
        }
    } else {
        // 未知命令，发送错误响应
        QJsonObject errorResponse;
        errorResponse["ret_code"] = -1;
        errorResponse["message"] = "Unknown command";
        QJsonDocument responseDoc(errorResponse);
        sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
    }
}

/*
 * \brief 处理来自客户端的命令请求
 * 
 * 根据命令类型执行相应的操作，目前支持日志下载命令
 * 
 * \param clientSocket 客户端套接字指针
 * \param cmd 命令码
 * \param json JSON格式的请求数据
 * \param reqId 请求ID，用于匹配请求和响应
 */
void RobotTCPServer::handleCommand(QTcpSocket* clientSocket, uint16_t cmd, const QJsonObject& json, uint16_t reqId)
{
    LOG_INFO("处理命令:{} 数据:{}",cmd, QJsonDocument(json).toJson(QJsonDocument::Compact).toStdString());
    
    switch(cmd) {
    case RMS_CLIENT_CMD_LOG_DOWNLOAD: // 传递日志文件压缩包
    {
        // 调用日志打包和发送函数
        packageAndSendLogs(clientSocket, cmd, reqId);
        break;
    }
    case RMS_CLIENT_CMD_LOG_FILE_CLEAN: // 清理日志文件
    {
        // 调用日志清理函数
        clearLogFiles(clientSocket, cmd, reqId);
        break;
    }
    default:
    {
        QJsonObject errorResponse;
        errorResponse["info_msg"] = QString("Unsupported  command !");
        errorResponse["ret_code"] = -1;
        QJsonDocument responseDoc(errorResponse);
        sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
        return;
    }
    }
}

/*
 * \brief 向客户端发送响应数据
 * 
 * 该函数用于构建并发送响应数据包到指定的客户端。它会创建符合协议格式的响应数据，
 * 并在客户端套接字处于连接状态时发送数据。
 * 
 * \param clientSocket 客户端套接字指针，用于发送数据
 * \param cmd 命令码，标识响应的命令类型
 * \param jsonData JSON格式的响应数据
 * \param binaryData 二进制格式的响应数据
 * \param reqId 请求ID，用于匹配请求和响应
 */
void RobotTCPServer::sendResponse(QTcpSocket* clientSocket, uint16_t cmd, const QByteArray& jsonData, const QByteArray& binaryData, uint16_t reqId)
{
    QByteArray responseData = createResponseData(cmd, jsonData, binaryData, reqId);
    
    if (clientSocket && clientSocket->state() == QAbstractSocket::ConnectedState) {
        clientSocket->write(responseData);
        LOG_INFO("发送响应 - 命令:{} 请求ID:{} 数据大小:{}",cmd, reqId,responseData.size());
    }
}

/**
 * @brief 创建响应数据包
 * 
 * 将命令码、JSON数据和二进制数据打包成Seer协议格式的响应数据
 * 
 * @param cmd 命令码
 * @param jsonData JSON格式的数据
 * @param binaryData 二进制数据
 * @param reqId 请求ID
 * @return QByteArray 打包后的响应数据
 */
QByteArray RobotTCPServer::createResponseData(uint16_t cmd, const QByteArray& jsonData, 
                                          const QByteArray& binaryData, uint16_t reqId)
{
    QByteArray totalData = jsonData + binaryData;
    uint32_t dataLength = totalData.size();
    
    // 使用QByteArray存储SeerData，确保内存管理正确
    QByteArray buffer;
    buffer.resize(sizeof(SeerHeader) + dataLength);
    
    SeerData* seerData = reinterpret_cast<SeerData*>(buffer.data());
    
    int size = seerData->setData(cmd,
                                reinterpret_cast<const uint8_t*>(totalData.data()),
                                dataLength,
                                static_cast<uint16_t>(jsonData.size()),
                                reqId, 0);
    
    buffer.resize(size);
    return buffer;
}

void RobotTCPServer::onSendResponseTimeout()
{
    // 响应超时处理
    LOG_INFO("响应发送超时!");
}

/**
 * @brief 打包并发送日志文件给客户端
 * 
 * 该函数负责将"data/logs"目录下的日志文件打包成tar.gz格式的压缩文件，
 * 然后通过TCP套接字发送给客户端。
 * 
 * @param clientSocket 客户端TCP套接字指针，用于发送数据
 * @param cmd 命令类型标识符
 * @param reqId 请求ID，用于匹配请求和响应
 * @return bool 操作成功返回true，失败返回false
 */
bool RobotTCPServer::packageAndSendLogs(QTcpSocket* clientSocket, uint16_t cmd, uint16_t reqId)
{
    // 检查data/logs目录是否存在
    QDir logsDir("data/logs");
    if (!logsDir.exists()) {
        // 发送错误响应，使用统一的JSON格式
        QJsonObject errorResponse;
        errorResponse["info_msg"] = QString("Logs directory does not exist");
        errorResponse["ret_code"] = -1;
        QJsonDocument responseDoc(errorResponse);
        sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
        return false;
    }
    
    // 创建临时压缩文件路径
    QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
    QString zipFilePath = QString("data/logs_%1.tar.gz").arg(timestamp);
    
    // 使用系统tar命令压缩日志目录
    QString command = QString("tar -czf %1 -C data logs").arg(zipFilePath);
    LOG_INFO("执行命令:{}",command.toStdString());
    int result = system(command.toStdString().c_str());
    
    // 检查压缩操作是否成功并且文件存在
    if (result == 0 && QFile::exists(zipFilePath)) {
        // 读取整个文件内容
        QFile zipFile(zipFilePath);
        if (zipFile.open(QIODevice::ReadOnly)) {
            QByteArray fileData = zipFile.readAll();
            zipFile.close();
            
            // 直接发送文件数据，不包含JSON部分
            sendResponse(clientSocket, cmd + 10000, QByteArray(), fileData, reqId);

            // 发送成功后删除临时压缩文件以节省存储空间
            QFile::remove(zipFilePath);
            LOG_INFO("日志压缩包发送成功;已删除临时压缩文件: {}", zipFilePath.toStdString());
            return true;
        } else {
            // 读取失败时也删除临时文件
            QFile::remove(zipFilePath);
            
            // 发送错误响应，使用统一的JSON格式
            QJsonObject errorResponse;
            errorResponse["info_msg"] = QString("Failed to open logs package for reading");
            errorResponse["ret_code"] = -1;
            QJsonDocument responseDoc(errorResponse);
            sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
            LOG_ERROR("日志压缩包发送失败;无法打开临时压缩文件: {}", zipFilePath.toStdString());
            return false;
        }
    } else {
        // 确保临时文件被删除（即使创建失败也可能有残留）
        if (QFile::exists(zipFilePath)) {
            QFile::remove(zipFilePath);
        }
        
        // 发送错误响应，使用统一的JSON格式
        QJsonObject errorResponse;
        errorResponse["info_msg"] = QString("Failed to create logs package");
        errorResponse["ret_code"] = -1;
        QJsonDocument responseDoc(errorResponse);
        sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
        LOG_ERROR("日志压缩包发送失败;无法创建临时压缩文件: {}", zipFilePath.toStdString());
        return false;
    }
}

/**
 * @brief 清理日志目录下的log和txt文件
 * 
 * 该函数负责清理"data/logs"目录下的所有.log和.txt文件，
 * 并向客户端发送操作结果。
 * 
 * @param clientSocket 客户端TCP套接字指针，用于发送数据
 * @param cmd 命令类型标识符
 * @param reqId 请求ID，用于匹配请求和响应
 * @return bool 操作成功返回true，失败返回false
 */
bool RobotTCPServer::clearLogFiles(QTcpSocket* clientSocket, uint16_t cmd, uint16_t reqId)
{
    // 检查data/logs目录是否存在，使用绝对路径避免目录遍历问题
    QDir logsDir("data/logs");
    
    if (!logsDir.exists()) {
        QJsonObject responseJson;
        // 发送错误响应
        responseJson["info_msg"] = QString("Failed to delete files！Logs directory does not exist！");
        responseJson["ret_code"] = -1;  // 错误码用-1表示有错误
        QJsonDocument responseDoc(responseJson);
        sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
        return false;
    }
    
    // 获取目录下所有文件
    QStringList filters;
    filters << "*.log" << "*.txt";
    QFileInfoList fileList = logsDir.entryInfoList(filters, QDir::Files);
    
    int deletedCount = 0;
    QString errorFiles;
    
    // 删除符合条件的文件
    for (const QFileInfo& fileInfo : fileList) {
        QString filePath = fileInfo.absoluteFilePath();
        if (!QFile::remove(filePath)) {
            if (!errorFiles.isEmpty()) {
                errorFiles += ", ";
            }
            errorFiles += fileInfo.fileName();
        } else {
            deletedCount++;
        }
    }
    
    QJsonObject responseJson;
    // 构造响应JSON，按照指定格式
    if (!errorFiles.isEmpty()) {
        responseJson["info_msg"] = QString("Failed to delete files: %1").arg(errorFiles);
        responseJson["ret_code"] = -1;  // 错误码用-1表示有错误
    } else {
        responseJson["info_msg"] = QString("Successfully deleted %1 log files").arg(deletedCount);
        responseJson["ret_code"] = 0;   // 成功码用0表示无错误
    }
    
    QJsonDocument responseDoc(responseJson);
    
    // 先记录日志再发送响应，保证操作原子性
    LOG_INFO("清理日志文件完成;删除文件数: {};状态: {}", 
                  deletedCount, 
                  responseJson["info_msg"].toString().toStdString());
    
    sendResponse(clientSocket, cmd + 10000, responseDoc.toJson(), QByteArray(), reqId);
    
    return true;
}

int main(int argc, char *argv[])
{
    // 初始化日志系统
    GlobalLogger::getInstance().initialize("data/logs", "RMSClient_tcp", 5);
    
    // 初始化Qt应用
    QCoreApplication app(argc, argv);
    
    rclcpp::init(argc, argv);
    
    // 创建集成的ROS2节点
    auto node = std::make_shared<RobotTCPServer>();
    
    // 从参数服务器获取端口号
    int port = node->declare_parameter("port", RMS_CLIENT_LOG_DOWNLOAD_PORT);
    
    // 启动服务器，监听端口
    if (!node->startServer(port)) {
        LOG_ERROR("服务器启动失败");
        return -1;
    }
    
    LOG_INFO("服务器运行中，监听端口:{}", port);
    
    // 使用单线程执行器处理ROS 2回调
    rclcpp::executors::SingleThreadedExecutor executor;
    executor.add_node(node);
    
    // 创建一个QTimer来定期调用executor的spin_some
    QTimer timer;
    timer.setInterval(10); // 10ms
    QObject::connect(&timer, &QTimer::timeout, [&executor]() {
        executor.spin_some();
    });
    timer.start();
    
    // Qt事件循环在主线程中运行，同时定期处理ROS 2回调
    int result = app.exec();
    
    // 关闭服务器
    node->stopServer();
    rclcpp::shutdown();
    
    return result;
}


#include "moc_robot_tcp_server.cpp"